﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Lesson11
{

    // 泛型
    #region 泛型
    // 什么是泛型
    // 泛型实现了类型参数化，达到代码重用目的
    // 通过类型参数化来实现同一份代码上操作多种类型
    // 泛型相当于类型占位符
    // 定义类或方法时使用替代符代表变量类型
    // 当真正使用类或者方法时再指定类型

    // 泛型分类
    // 泛型类和泛型接口
    // 基本语法：
    // Class 类名<泛型占位字母>
    // interface 接口名<泛型占位字母>

    // 泛型函数
    // 基本语法：函数名<泛型占位字母>(参数列表)
    // 注意：泛型占位字母可以有多个，用逗号隔开

    // 泛型类和接口
    class TestClass<T>
    {
        public T value;
    }

    interface TestInterface<T>
    {
        T value { get; set; }
    }

    // 使用类去实现接口的时候必须声明是什么类型
    class TestA : TestInterface<int>
    {
        public int value { get; set; }
    }

    // 泛型方法
    // 普通类中的泛型方法
    class TestB
    {
        public void GetFun<T>(T value)
        {
            Console.WriteLine(value);
        }

        public void GetFun<T>()
        {
            // 可以用泛型  做一些逻辑处理
            //T t = null;   // 报错  不知道 T 的类型不能直接赋值
            //T t = 0;  // 报错 不能直接赋值
            T t = default(T);            // default()函数是用来获取默认值的
        }

        // 用在返回值上
        public T TestFun<T>()
        {
            return default(T);
        }

        // 多个
        public void TestFun<T, E, M>(T t, E e, M m)
        {

        }
    }

    // 泛型类中的泛型方法   加了泛型之后，即使类名重复也是不同的类
    class TestB<T>
    {
        // 这个不叫泛型方法 因为 T 是泛型类声明时就指定了，固定下来了 不能改了
        public void TestFun(T t)
        {
            Console.WriteLine(t);
        }

        // 这个是泛型方法
        public void TestFun<E>(E e)
        {
            Console.WriteLine($"{e}");
        }
    }


    #endregion

    #region 泛型约束
    // 泛型类
    class TestClass<T, U>
    {
        public T t;
        public U u;

        public U TestFun(T t)
        {
            return default(U);
        }

        // 泛型函数
        public V TestFun<K, V>(K k)
        {
            return default(V);
        }
    }

    // 泛型约束是让泛型有一定的限制
    // 关键字：where
    // 语法：where 泛型字母:(约束类型)
    // 泛型约束一共有6种
    // 1、值类型                  where 泛型字母：struct
    // 2、引用类型                where 泛型字母：class
    // 3、存在无参公共构造函数     where 泛型字母：new()
    // 4、某个类本身或者其派生类   where 泛型字母：类名
    // 5、某个接口的派生类型      where 泛型字母：接口名
    // 6、另一个泛型类型本身或者派生类型   where 泛型字母：另一个泛型字母

    // 值类型约束
    class Test1<T> where T : struct
    {
        public T value;
        public void TestFun<K>() where K : struct
        {

        }
    }
    // 引用类型约束
    class Test2<T> where T : class
    {
        public T value;
        public void TestFun<K>(K k) where K : class
        {
            Console.WriteLine($"{k} {value}");
        }
    }
    // 公共无参构造约束
    class Test3<T> where T : new()
    {
        public T value;
        public void TestFun<K>(K k) where K: new()
        {

        }
    }

    // 类约束，直接写类名，T 和 K的传值必须是 TestC1类或其派生类
    class Test4<T> where T:TestC1
    {
        public T value;
        public void TestFun<K>(K k) where K : TestC1
        {

        }
    }

    // 接口约束
    interface Ifly
    {

    }
    // 这个是需要Iflay接口的派生类型，实现了这个接口的类
    class Test5<T> where T:Ifly
    {
        public T value;
        public void TestFun<K>(K k) where K : Ifly
        {

        }
    }

    // 另一个泛型约束
    class Test6<T, U> where T:U
    {
        public T value;
        public void TestFun<K, V>() where K:V
        {

        }
    }

    class TestC1
    {

    }
    class TestC2
    {
        public TestC2(int i)
        {
        }
    }
    class TestC3
    {
        private TestC3()
        {
        }
    }
    class TestC4:Ifly
    {

    }


    // 约束的组合使用  两个约束通过逗号分开 不是所有的约束都能组合使用，还有顺序的
    class Test7<T> where T:class, new()
    {
        public T value;
    }

    // 多个泛型有约束
    class Test8<T, k> where T:class, new() where k:struct
    {
        public T value;
    }
    #endregion

    internal class Program
    {
        static void Main(string[] args)
        {
            // c#中的简单数据结构类
            #region ArrayList
            // ArrayList是一个c#为我们封装好的类
            // 它的本质是一个object类型的数组
            // ArrayList是一个动态数组，底层是一个object类型的数组
            // ArrayList类帮我们实现了很多方法，比如数组的增删改查


            // 声明一个ArrayList对象, 需要先引用System.Collections命名空间
            ArrayList arrayList = new ArrayList();

            // 增删改查
            // 添加元素 增  Add
            arrayList.Add(1);
            arrayList.Add("hello");   // 可以存任意类型
            arrayList.Add(3.14);
            arrayList.Add(new object());

            ArrayList arrayList2 = new ArrayList();
            arrayList2.AddRange(arrayList); // 批量添加，增加范围，拼接的意思

            // 插入元素 Insert
            arrayList.Insert(1, true); // 在指定位置插入元素

            // 删除元素 删  Remove
            arrayList.Remove(1); // 移除指定元素   从头找，找到第一个就删除，不再继续找了
            // 移除指定位置的元素 RemoveAt
            arrayList.RemoveAt(2);
            // 清空元素 Clear
            //arrayList.Clear(); // 清空所有元素

            // 查找元素 查  Contains
            Console.WriteLine(arrayList[0]);  // 获取指定位置的元素
            // 查看元素是否存在 Contains
            if(arrayList.Contains("hello"))
            {
                Console.WriteLine("存在");
            }
            else
            {
                Console.WriteLine("不存在");
            }
            arrayList.Add("hello");
            // 正向查找元素的索引 IndexOf
            int index = arrayList.IndexOf("hello");  // 返回第一个hello的索引位置
            Console.WriteLine(index);
            // 反向查找元素的索引 LastIndexOf
            int index2 = arrayList.LastIndexOf("hello"); // 返回最后一个hello的索引位置, 计数还是从前面开始的
            Console.WriteLine(index2);

            // 修改元素 改  
            arrayList[0] = 100; // 修改指定位置的元素

            // 遍历元素
            // 获取元素的个数 Count
            Console.WriteLine(arrayList.Count); // 获取元素的个数

            // 容量 Capacity
            Console.WriteLine(arrayList.Capacity); // 获取容量

            for (int ii1 = 0; ii1 < arrayList.Count; ii1++)
            {
                Console.WriteLine(arrayList[ii1]);
            }

            // foreach循环 类里实现迭代器，才可以用foreach循环
            foreach (var item in arrayList)
            {
                Console.WriteLine(item);
            }

            // 装箱拆箱
            // ArrayList本质上是一个可以自动扩容的object数组
            // 用万物之父object来存储数据，自然存在装箱拆箱
            // 当往其中进行值类型存储时就是在装箱，当将值类型对象取出来转换使用时就是拆箱
            // 所以ArrayList尽量少用，会有更好的数据容器

            int k = 1;
            arrayList[0] = k; // 装箱

            k = (int)arrayList[0]; // 拆箱 需要强转
                                   // 装箱拆箱是堆栈转换，消耗性能

            #endregion

            #region Stack
            // Stack 栈 是c#为我们封装好的类
            // 它的本质是一个object[]数组，只是封装了特殊的存储规则
            // Stack是一个先进后出、后进先出（LIFO）的数据结构
            // Stack是栈存储容器，存储数据时只能从栈顶进行存取

            // 声明 引入命名空间 System.Collections
            Stack stack = new Stack();

            // 增取改查
            // 添加元素 增  压栈 Push
            stack.Push(1);
            stack.Push("hello");
            stack.Push(3.14);
            stack.Push(new object());
            stack.Push(true);
            stack.Push(1);

            // 去除元素 取  弹栈 Pop
            object v = stack.Pop(); // 弹出栈顶元素
            Console.WriteLine(v);

            v = stack.Pop(); 
            Console.WriteLine(v);

            // 查 Peek
            // 栈无法查看指定位置的元素，只能查看栈顶元素 
            object s = stack.Peek(); // 查看栈顶元素
            Console.WriteLine(s);

            // 查看元素是否存在 Contains
            if (stack.Contains("hello"))
            {
                Console.WriteLine("存在");
            }
            else
            {
                Console.WriteLine("不存在");
            }

            // 改
            // 栈无法修改其中的元素，只能压栈和弹栈
            // 只能清空
            stack.Clear(); // 清空栈中的元素

            // 遍历元素
            stack.Push(1.2f);
            stack.Push("123");
            // 先获取长度
            int count = stack.Count;
            Console.WriteLine(count);

            // 栈无法用for循环遍历
            // 只能用foreach循环遍历
            foreach (object item in stack)
            {
                Console.WriteLine(item);  // 栈顶元素在前面，从栈顶开始遍历到栈底结束
            }

            // 还有一种遍历方式，把栈转换成object数组
            object[] array = stack.ToArray(); // 转换成object数组
            for (int ii = 0; ii < array.Length; ii++)
            {
                Console.WriteLine(array[ii]); // 从栈底开始遍历到栈顶结束
            }

            // 循环弹栈   循环结束后栈会清空
            while (stack.Count > 0)
            {
                Console.WriteLine(stack.Pop());
            }
            Console.WriteLine(stack.Count);

            // 装箱拆箱
            // 万物之父存储数据，自然存在装箱拆箱
            #endregion

            #region Queue
            // 队列 Queue是一个c#为我们封装好的类
            // 它的本质是一个object[]数组，只是封装了特殊的存储规则

            // Queue是一个先进先出、后进后出（FIFO）的数据结构

            // 声明 需要引入命名空间 System.Collections
            Queue queue = new Queue();

            // 增取改查
            // 添加元素 增  入列 Enqueue
            queue.Enqueue(1);
            queue.Enqueue("hello");
            queue.Enqueue(3.14);
            queue.Enqueue(new object());
            queue.Enqueue(true);
            queue.Enqueue(1.5f);

            // 取出元素 取  出列 Dequeue
            object v2 = queue.Dequeue(); // 出队列，出队列是从队头开始的
            Console.WriteLine(v2);
            v2 = queue.Dequeue(); // 出队列
            Console.WriteLine(v2);

            // 查 Peek
            // 队列无法查看指定位置的元素，只能查看列头元素
            object s2 = queue.Peek(); // 查看列头元素
            Console.WriteLine(s2);

            // 查看元素是否存在 Contains
            if (queue.Contains("hello"))
            {
                Console.WriteLine("存在 hello");
            }
            else
            {
                Console.WriteLine("不存在 hello");
            }

            // 改
            // 队列无法修改其中的元素，只能入队(列)和出队(列)
            // 只能清空
            //queue.Clear(); // 清空队列中的元素

            // 遍历元素
            // 先获取长度
            Console.WriteLine(queue.Count);
            foreach (object item in queue)
            {
                Console.WriteLine(item);  // 队头元素在前面，从队头开始遍历到队尾结束
            }

            // 还有一种遍历方式，把队列转换成object数组
            object[] array2 = queue.ToArray(); // 转换成object数组
            for (int i1 = 0; i1 < array2.Length; i1++)
            {
                Console.WriteLine(array2[i1]); // 从列头开始遍历到列尾结束
            }

            // 循环出列   循环结束后队列会清空
            while (queue.Count > 0)
            {
                Console.WriteLine(queue.Dequeue());
            }


            // 装箱拆箱

            #endregion

            #region Hashtable
            // 哈希表 Hashtable 又称散列表，是基于键的哈希代码组织起来的  键/值对
            // 主要作用是提高数据查询的效率
            // 使用键访问元素中的集合
            // key-value结构

            // 声明 需要引入命名空间 System.Collections
            Hashtable hashtable = new Hashtable();

            // 增取改查
            // 增  Add(键, 值)  键和值都可以是object类型
            hashtable.Add(1, "hello");
            hashtable.Add("1", 2);
            hashtable.Add(true, false);
            // 注意不能出现相同的键
            //hashtable.Add(1, "msg"); 运行会报错

            // 删 只能通过键去删除
            hashtable.Remove(1);   // 移除一组键值对

            // 删除不存在的键，没反应
            hashtable.Remove("2");

            // 或者直接清空
            hashtable.Clear();

            hashtable.Add(1, 123);
            hashtable.Add("1", "123");
            // 查
            // 1、通过键查值，找不到会返回空
            Console.WriteLine(hashtable[1]);   // 中括号中的1就是键
            Console.WriteLine(hashtable["2"]);  // 找不到返回空 控制台会空出一行

            // 2、查看是否存在
            //  根据键查询
            if (hashtable.Contains(1))
            {
                Console.WriteLine("存在键为1的键值对");
            }
            if(hashtable.ContainsKey("2"))  // 更语义化
            {
                Console.WriteLine("存在键为\"2\"的键值对");
            }

            // 根据值去查询
            if (hashtable.ContainsValue("3"))
            {
                Console.WriteLine("存在值为\"3\"的键值对");
            }
            else
            {
                Console.WriteLine("不存在值为\"3\"的键值对");
            }

            // 改
            // 只能改 键对应的值的内容，无法修改键
            Console.WriteLine(hashtable[1]);
            hashtable[1] = 100;
            Console.WriteLine(hashtable[1]);

            // 遍历
            // hashtable的长度是键值对的对数
            Console.WriteLine(hashtable.Count);

            // 遍历所有的键
            foreach (object item in hashtable.Keys)
            {
                Console.WriteLine("键：" + item);
                Console.WriteLine("值" + hashtable[item]);
                Console.WriteLine("{0} - {1}", item, hashtable[item]);
            }

            // 遍历所有的值
            foreach (object item in hashtable.Values)
            {
                Console.WriteLine(item);
            }

            // 键值对一起遍历
            foreach(DictionaryEntry item in hashtable)
            {
                Console.WriteLine("键：" + item.Key + "- 值：" + item.Value);
            }

            // 迭代器遍历法
            IDictionaryEnumerator enumerator = hashtable.GetEnumerator();
            while (enumerator.MoveNext())
            {
                Console.WriteLine(enumerator.Key + "-" + enumerator.Value);
            }


            // 装箱拆箱

            #endregion

            #region 泛型
            // 泛型使用 泛型类
            TestClass<String> t = new TestClass<string>();   // 这边声明的时候赋什么类型，类里面就会是什么样的类型的值
            t.value = "hello";
            //t.value = 1;   // 不是string会报错
            Console.WriteLine(t.value);

            TestB testB = new TestB();
            testB.GetFun<string>("123456");
            //testB.GetFun<string>(123456);   // 赋string类型了，传数字会报错
            testB.GetFun<int>(123);

            TestB<int> testB1 = new TestB<int>();
            testB1.TestFun(10);   // 这不是泛型方法，只能传int类型的

            testB1.TestFun<string>("123");  // 这是泛型方法了 可以变化传值类型
            testB1.TestFun<float>(3.14f);
            testB1.TestFun(true);        // 省略赋值的尖括号，会根据后面传的具体值去判断用的什么方法

            // 泛型的作用
            // 1、不同类型对象的相同逻辑处理就可以选择泛型
            // 2、使用泛型可以一定程度避免装箱拆箱

            // 优化ArrayList

            TestClass<string, int> t0 = new TestClass<string, int>();
            t0.t = "123";
            t0.u = 123;

            t0.TestFun("123");

            t0.TestFun<float, double>(9.8f);

            // 泛型约束
            //Test1<object> t1 = new Test1<object>();  // 报错，因为加了值类型泛型约束，泛型不能为空（null） 所以要用值类型
            Test1<int> t1 = new Test1<int>();
            //t1.TestFun<object>();              // 同样的报错，泛型不能是null
            t1.TestFun<float>();

            //Test2<int> t2 = new Test2<int>();   // 报错，必须是引用类型
            Test2<object> t2 = new Test2<object>();
            t2.TestFun<Random>(new Random());

            Test3<TestC1> test3 = new Test3<TestC1>(); // 没问题， TestC1中有无参构造
            //Test3<TestC2> test31 = new Test3<TestC2>();  // 报错，TestC2中是有参构造 
            //Test3<TestC3> test32 = new Test3<TestC3>();   // 报错，TestC3中是私有的构造函数

            Test4<TestC1> test4 = new Test4<TestC1>();  // 可以
            //Test4<TestC2> test4 = new Test4<TestC2>();  // 不行

            Test5<Ifly> testi = new Test5<Ifly>(); // 可以
            Test5<TestC4> test5 = new Test5<TestC4>(); // 可以
            //Test5<TestC3> test5 = new Test5<TestC3>(); // 不行

            Test6<TestC4, Ifly> test6 = new Test6<TestC4, Ifly>();  // 可以，TestC4实现了Iflay，是Iflay的派生类
            #endregion

            // 泛型数据结构
            #region List
            // List的本质
            // List是一个c#为我们封装好的类
            // 本质是一个可变类型的泛型数组
            // List类实现了很多方法，比如泛型数组的增删改查

            // 声明 需要引入命名空间 System.Collections.Generic
            List<int> list = new List<int>();
            List<string> list2 = new List<string>();

            // 增删查改   方法跟 ArrayList一样
            list.Add(21);
            list.Add(123);
            list2.Add("123");
            list2.Add("abc");

            List<string> listStr = new List<string>();
            listStr.Add("123");
            listStr.AddRange(list2);

            // 插入，在指定位置插入元素
            list.Insert(1, 1080);

            // 删除
            // 移除指定元素
            list.Remove(21);
            // 移除指定位置的元素
            list2.RemoveAt(0);
            // 清空
            listStr.Clear();

            // 查
            // 查看指定位置的元素
            Console.WriteLine(list[0]);

            // 查询元素是否存在
            if(list2.Contains("abc"))
            {
                Console.WriteLine("存在abc");
            }
            else
            {
                Console.WriteLine("不存在abc");
            }

            list.Add(520);
            // 正向查找元素的位置下标  找不到返回-1
            int i = list.IndexOf(520);
            Console.WriteLine(i);
            // 反向查找   但还是从头开始算位置
            i = list.LastIndexOf(123);
            Console.WriteLine(i);

            // 改
            list[0] = 99;
            Console.WriteLine(list[0]);

            // 遍历
            // 长度
            Console.WriteLine(list.Count);
            // 容量
            Console.WriteLine(list.Capacity);

            for(int j  = 0; j < list.Count; j++)
            {
                Console.WriteLine(list[i]);
            }

            foreach(int item in list)
            {
                Console.WriteLine(item);
            }


            #endregion

            #region Dictionary
            // 字典 可以看做是 拥有泛型的Hashtable
            // 它也是基于键的哈希码组织起来的 键/值
            // 键值对类型 从Hashtable的object变为了可以自己指定的泛型

            // 声明
            // 需要引用命名空间 using System.Collections.Generic
            Dictionary<int, string> dict = new Dictionary<int, string>();

            // 增删查改
            // 不能有相同的键
            dict.Add(1, "a");
            dict.Add(2, "b");
            dict.Add(3, "c");
            dict.Add(4, "d");

            // 删
            // 只能通过键去删除  删除不存在的键 没反应
            dict.Remove(1);
            dict.Remove(5);

            // 清空
            dict.Clear();

            dict.Add(1, "abc");
            dict.Add(2, "ad");
            dict.Add(3, "ckg");
            dict.Add(4, "def");
            // 查
            // 根据键查询值
            Console.WriteLine(dict[1]); // "abc"
            //Console.WriteLine(dict[10]); // 找不到直接报错

            // 查看是否存在
            if (dict.ContainsKey(1))
            {
                Console.WriteLine("存在键为1的，值为：" + dict[1]);
            }
            if (dict.ContainsValue("aaa"))
            {
                Console.WriteLine("存在值为 aaa 的");
            }

            // 改
            dict[4] = "sot";  // 将键为4的键值对 值改成  sot
            Console.WriteLine(dict[4]);

            // 遍历
            // 获取长度
            Console.WriteLine(dict.Count);
            // 遍历所有键
            foreach(int item in dict.Keys)
            {
                Console.WriteLine(item + "-" + dict[item]);
            }

            // 遍历所有值
            foreach (string item in dict.Values)
            {
                Console.WriteLine(item);
            }

            // 键值对一起遍历
            foreach(KeyValuePair<int, string> item in dict)
            {
                Console.WriteLine(item.Key + "-" + item.Value);
            }



            #endregion

            #region 顺序存储和链式存储
            // 数据结构
            // 数据结构式计算机存储、组织数据的方式（规则）
            // 数据结构是指相互之间存在一种或多种特定关系的数据元素的集合

            // 常用的数据结构
            // 数组、栈、队列、链表、树、图、堆、散列表

            // 线性表
            //线性表是一种数据结构，是由n个具有相同特性的数据元素的有限序列
            // 比如数组、ArrayList、Stack、Queue、链表等

            // 顺序存储和链式存储 是数据结构中的两种存储结构

            // 顺序存储
            // 数组、Stack、Queue、List、ArrayList - 顺序存储
            // 只是 数组、Stack、Queue的组织规则不同而已
            // 顺序存储： 用一组地址连续的存储单元依次存储线性表的各个数据元素

            // 链式存储
            // 单向链表、双向链表、循环链表 - 链式存储
            // 链式存储：用一组任意的存储单元存储线性表中的各个数据元素  （链接存储）

            // 以上的点可以画图理解

            // 实现一个最简单的单向链表
            // 关联数据
            LinkedNode<int> linkedNode = new LinkedNode<int>(1); // 创建一个节点
            LinkedNode<int> linkedNode2 = new LinkedNode<int>(2); // 创建下一个节点
            linkedNode.next = linkedNode2;   // 关联上，将下一个节点的引用赋值给当前节点的next属性
            linkedNode2.next = new LinkedNode<int>(3);
            linkedNode2.next.next = new LinkedNode<int>(4);
            linkedNode2.next.next.next = null;   // 最后一个节点的next属性赋值为null

            CustomLinkedList<int> linkedList = new CustomLinkedList<int>(); // 创建一个链表对象
            linkedList.Add(1); // 添加节点
            linkedList.Add(2); // 添加节点
            linkedList.Add(3); // 添加节点
            linkedList.Add(4); // 添加节点
            LinkedNode<int> node = linkedList.header; // 获取头节点
            while (node != null) // 遍历链表
            {
                Console.WriteLine(node.value); // 打印节点的值
                node = node.next; // 指向下一个节点
            }
            linkedList.Remove(2); // 删除节点
            node = linkedList.header; // 获取头节点
            while (node != null)  // 遍历链表
            {
                Console.WriteLine(node.value);
                node = node.next; // 指向下一个节点
            }
            linkedList.Remove(1); // 删除节点
            node = linkedList.header; // 获取头节点
            while (node != null)   // 遍历链表
            {
                Console.WriteLine(node.value);
                node = node.next; // 指向下一个节点
            }
            linkedList.Remove(4); // 删除节点
            node = linkedList.header; // 获取头节点
            while (node != null)    // 遍历链表
            {
                Console.WriteLine(node.value);
                node = node.next; // 指向下一个节点
            }
            linkedList.Add(99);  // 添加节点
            node = linkedList.header; // 获取头节点
            while (node != null)   // 遍历链表
            {
                Console.WriteLine(node.value);
                node = node.next; // 指向下一个节点
            }

            // 顺序存储和链式存储的优缺点
            // 增： 链式存储 计算上 优于顺序存储 （中间插入时链表不用像顺序一样去移动位置）
            // 删： 链式存储 计算上 优于顺序存储 （中间删除时链表不用像顺序一样去移动位置）
            // 查： 顺序存储 计算上 优于链式存储 （顺序存储可以直接通过下标访问，链表需要遍历）
            // 改： 顺序存储 计算上 优于链式存储 （顺序存储可以直接通过下标访问，链表需要遍历）

            #endregion


            #region LinkedList
            // LinkedList本质是一个可变类型的泛型双向链表
            // 声明   需要引入命名空间 System.Collections.Generic
            LinkedList<int> linkedList2 = new LinkedList<int>();
            // 链表对象，需要掌握两个类
            // 链表类本身和链表节点类 LinkedListNode

            // 增删查改
            // 增
            linkedList2.AddLast(1); // 在链表尾部添加元素
            linkedList2.AddFirst(2); // 在链表头部添加元素
            // 在某一节点后添加元素 AddAfter
            LinkedListNode<int> n = linkedList2.Find(1); // 要先查找到某个元素
            linkedList2.AddAfter(n, 3); // 在某一节点后添加元素 AddAfter

            // 在某一节点前添加元素 AddBefore
            LinkedListNode<int> n2 = linkedList2.Find(3); // 要先查找到某个元素
            linkedList2.AddBefore(n2, 4); // 在某一节点前添加元素 AddBefore

            // 删除
            linkedList2.RemoveFirst(); // 删除头部节点
            linkedList2.RemoveLast(); // 删除尾部节点

            linkedList2.Remove(1); // 删除指定元素的节点

            // 清空
            linkedList2.Clear(); // 清空链表

            linkedList2.AddLast(10);
            linkedList2.AddLast(15);
            linkedList2.AddLast(12);
            linkedList2.AddLast(15);
            linkedList2.AddLast(20);

            // 查
            // 查看头节点
            LinkedListNode<int> firstNode = linkedList2.First; // 获取头节点
            Console.WriteLine(firstNode.Value);  // 打印值
            // 查看尾节点
            LinkedListNode<int> lastNode = linkedList2.Last; // 获取尾节点
            Console.WriteLine(lastNode.Value);  // 打印值

            // 查看指定元素的节点
            LinkedListNode<int> node1 = linkedList2.Find(1); // 查找指定元素的节点,找不到就返回null
            if (node1 != null)
            {
                Console.WriteLine(node1.Value); // 打印值
            }
            else
            {
                Console.WriteLine("不存在该节点");
            }

            // 判断是否存在某元素
            if (linkedList2.Contains(1))
            {
                Console.WriteLine("存在值为'1'的元素");
            }
            else
            {
                Console.WriteLine("不存在值为'1'元素");
            }

            // 改
            // 要先查找到节点再修改其中的值
            linkedList2.First.Value = 100; // 修改头节点的值


            // 遍历
            // foreach循环遍历
            foreach (int item in linkedList2)
            {
                Console.WriteLine(item); // 直接得到值
            }

            Console.WriteLine("&&&&&&&&&&");
            // 通过节点遍历
            // 从头到尾
            LinkedListNode<int> head = linkedList2.First; // 获取头节点
            while (head != null)
            {
                Console.WriteLine(head.Value); // 打印值
                head = head.Next; // 指向下一个节点
            }
            Console.WriteLine("&&&&&&&&&&");
            // 从尾到头
            LinkedListNode<int> tail = linkedList2.Last; // 获取尾节点
            while (tail != null)
            {
                Console.WriteLine(tail.Value); // 打印值
                tail = tail.Previous; // 指向上一个节点
            }

            #endregion

            #region 泛型栈和队列
            // 数据容器
            // 变量
            // 复杂数据容器
            // 枚举 enum   结构体 struct   数组   类
            // 数据集合 System.Collections
            // ArrayList（object数据列表）  Stack（栈）  Queue（队列）  Hashtable（哈希表）
            // 泛型数据集合 System.Collections.Generic
            // List（泛型数据列表）  Dictionary（字典）  LinkedList（双向链表）  

            // 新的使用命名空间 System.Collections.Generic
            // Stack<T> （泛型栈） 先进后出  后进先出
            // Queue<T> （泛型队列） 先进先出  后进后出
            // 方法和之前一样的
            Stack<int> stack1 = new Stack<int>(); // 声明一个泛型栈
            stack1.Push(1); // 压栈
            stack1.Pop(); // 弹栈

            Queue<int> queue1 = new Queue<int>(); // 声明一个泛型队列
            queue1.Enqueue(1); // 入队
            queue1.Dequeue(); // 出队
            #endregion

        }
        // 泛型优化ArrayList
        class ArrayList<T>
        {
            private T[] array;
            public void Add(T value)
            {

            }
        }

        // 实现一个最简单的单向链表
        // 定义一个节点类
        class LinkedNode<T>
        {
            public T value; // 节点的值
            public LinkedNode<T> next; // 下一个节点的引用 存储下一个元素的地址
            public LinkedNode(T value)
            {
                this.value = value;
            }
        }

        // 单向链表类，管理单向链表节点
        class CustomLinkedList<T>
        {
            public LinkedNode<T> header;
            public LinkedNode<T> last; // 尾节点

            public void Add(T value)
            {
                // 添加节点
                LinkedNode<T> newNode = new LinkedNode<T>(value); // 创建一个新的节点
                if(header == null) // 如果头节点为空，说明链表为空
                {
                    header = newNode; // 将新节点赋值给头节点
                    last = newNode; // 将尾节点指向新节点
                }
                else
                {
                    // 上面还要定义一个last变量来保存当前节点
                    last.next = newNode; // 将新节点添加到最后一个节点的next属性中
                    last = newNode; // 新加后，尾节点就变成了新节点
                }
            }

            public void Remove(T value)  
            {
                if(header == null)
                {
                    return; // 链表为空，直接返回
                }
                if(header.value.Equals(value))  // 如果头节点的值等于要删除的值
                {
                    // 将下一个节点的地址赋值给头节点，那一个节点就变成了头节点，所以当前的头节点就变成了垃圾节点，没有引用指向它了，会被GC掉
                    header = header.next;
                    // 如果新的头节点是null，说明只有一个节点，那删除之后，链表就为空了
                    if (header == null)
                    {
                        last = null; // 如果链表为空，当前节点也要置空
                    }
                    return;
                }
                LinkedNode<T> temp = header; // 临时节点,指向头节点
                while (temp.next != null)
                {
                    // 如果下一个节点的地址不为空，就可以一直往下找
                    if(temp.next.value.Equals(value)) // 如果下一个节点的值等于要删除的值
                    {
                        // 将下一个节点的地址赋值给当前节点的next属性，那一个节点就变成了垃圾节点
                        temp.next = temp.next.next;
                        break;
                    }
                    temp = temp.next; // 当前节点指向下一个节点
                }
                // 如果删除的是最后一个节点，删除尾节点
                if (temp.next == null)
                {
                    last = temp; // 将当前节点赋值给尾节点
                    last.next = null; // 将尾节点的next属性置空
                }
            }
        }
    }
}
